
// ToolsLib Project

/* ToolsLib library for RusRoute firewall and other projects of
 * Andrey A. Moiseenko / IE Moiseenko A.A. (Russia)
 * e-mail: support@maasoftware.ru, maa2002@mail.ru
 * web: http://maasoftware.ru, http://maasoftware.com, http://maasoft.ru, http://maasoft.org
 * Author's full name: Andrey Alekseevitch Moiseenko
 * (russian name: Моисеенко Андрей Алексеевич)
 */

// ToolsLib/mthrlib2.h

/* Copyright (C) 2002-2024 Andrey A. Moiseenko (support@maasoftware.ru)
 * All rights reserved.
 *
 * This library contains cross-platform classes for working with
 * events, timed locks in Linux. Experimental code.
 * The library implementation written
 * by Andrey A. Moiseenko (support@maasoftware.ru).
 * This library and applications are
 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
 * as long as the following conditions are aheared to.
 *
 * Copyright remains Andrey A. Moiseenko, and as such any Copyright notices in
 * the code are not to be removed.  If this code is used in a product,
 * Andrey A. Moiseenko should be given attribution as the author of the parts used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Andrey A. Moiseenko (support@maasoftware.ru)
 *
 * THIS SOFTWARE IS PROVIDED BY ANDREY A. MOISEENKO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

inline CMaa_gLock_lib_Mutex* __GLock__lib(bool bInit = false) noexcept
{
    static CMaa_gLock_lib_Mutex* gptr = nullptr;
    if (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_lib_Mutex;
#ifndef TOOLSLIB_FAST_gLock_lib_Mutex
        gptr->AddRef();
#endif
}
    return gptr;
}
inline CMaaMutex* __GLock__lib2(bool bInit = false) noexcept
{
    static CMaaMutex* gptr = nullptr;
    if (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaMutex;
#ifndef TOOLSLIB_FAST_MUTEXES
        gptr->AddRef();
#endif
    }
    return gptr;
}
inline CMaa_gLock_usr_Mutex* __GLock__usr(bool bInit = false) noexcept
{
    static CMaa_gLock_usr_Mutex* gptr = nullptr;
    if (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_usr_Mutex;
#ifndef TOOLSLIB_FAST_gLock_usr_Mutex
        gptr->AddRef();
#endif
    }
    return gptr;
}
inline CMaaMutex* __GLock__usr2(bool bInit = false) noexcept
{
    static CMaaMutex* gptr = nullptr;
    if (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaMutex;
#ifndef TOOLSLIB_FAST_MUTEXES
        gptr->AddRef();
#endif
    }
    return gptr;
}
inline CMaa_gLock_usr3_Mutex* __GLock__usr3(bool bInit = false) noexcept
{
    static CMaa_gLock_usr3_Mutex* gptr = nullptr;
    if (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaa_gLock_usr3_Mutex;
#ifndef TOOLSLIB_FAST_gLock_usr3_Mutex
        gptr->AddRef();
#endif
    }
    return gptr;
}
inline CMaa_gLock_lib_Mutex& Get_gLock() noexcept
{
    return gLock_lib;
}


class CMaaEvent
{
#ifdef __unix__
    CMaaMutex1 m_Mutex;
    pthread_cond_t m_cond;
    bool m_bManualReset, m_bState;
#else
    CMaaMutex m_Mutex;
    bool m_bManualReset;
    HANDLE m_hEvent;
    //CMaaAutoInitObject<int, 0> m_ExternalEvent;
    int m_ExternalEvent = 0;
#endif
    int m_nWaiters, m_nWakeups, m_nPulseCount;
public:
    //CMaaEvent(bool bManualReset = false, bool bInitialState = false /*for ManualReset event*/);
    CMaaEvent(bool bManualReset = true, bool bInitialState = false /*for ManualReset event*/);
#ifdef _WIN32
    CMaaEvent(HANDLE h, bool bManualReset = true, int DoNotCloseEventHandle = 1);
    operator HANDLE () const noexcept
    {
        return m_hEvent;
    }
#endif
    ~CMaaEvent();
    void WakeOne() noexcept;
    void WakeAll() noexcept;
    bool Wait(_dword TimeMs = (_dword)-1) noexcept;
    bool Wait(_qword TimeUs) noexcept;
    bool Reset() noexcept; // Reset manual reset mutex
    bool Set() noexcept
    {
        WakeAll();
        return true;
    }
    bool Pulse() noexcept; // with race conditions
    /*
     bool Pulse() noexcept
     {
          if   (m_bManualReset)
          {
               // wake all and reset
          }
          else
          {
               // wake one and reset
          }
          return true;
     }
     */
};

class CMaaTimedMutex : public
#ifdef __unix__
CMaaMutex2
#else
CMaaMutex2
#endif
{
#ifdef __unix__
    pthread_cond_t m_cond;
    int m_Status; //number of recursive locks
    pthread_t m_curr;
#endif
public:
    CMaaTimedMutex() noexcept;
    ~CMaaTimedMutex() noexcept;
    bool Lock(_dword ms = (_dword)-1) noexcept;
    bool Lock(_qword us) noexcept;
    bool UnLock() noexcept;
};

class CMaaRWLock // native win32 & linux timed rw lock
{
    CMaaTimedMutex m_m1, m_m2;
    int m_AccessType; // -1 - write lock, 1 - read lock, 0 - not locked
    int m_n;
    bool m_hState;
    CMaaEvent m_h;
public:
    CMaaRWLock() noexcept;
    ~CMaaRWLock() noexcept;
    bool RLock(_dword Time = (_dword)-1, int n = 1) noexcept;
    bool RUnLock(int n = 1) noexcept;
    bool WLock(_dword Time = (_dword)-1) noexcept;
    void WUnLock() noexcept;
    void UnLock(int n = 1) noexcept; // Unlock Single Write lock or n Read locks
};

class CMaaRWLock2 // native win32 & linux not timed rw lock other linux implementation
{
#ifdef _WIN32
    CMaaRWLock m_Lock; // Linux can use CMaaRWLock too
#else
    pthread_mutex_t m_Mutex;
    pthread_cond_t m_CondRead, m_CondWrite;
    int m_Readers, m_Writers, m_Read_waiters, m_Write_waiters;
#endif
public:
    CMaaRWLock2() noexcept;
    ~CMaaRWLock2() noexcept;
    void RLock(int n = 1) noexcept;
    void RUnLock(int n = 1) noexcept;
    void WLock() noexcept;
    void WUnLock() noexcept;
};

#ifdef __unix__

template<class T> class CMaaWin32Locker
{
    T &m_Lock;
#ifdef _DEBUG_MTX
    CMaaString m_Name;
#else
#define m_Name CMaaStringZ
#endif
    int m_x;
    bool m_bUnLockerMode;
public:
    CMaaWin32Locker(T &Lock, const CMaaString
#ifndef _DEBUG_MTX
        &
#endif
        Name) noexcept
    :   m_Lock(Lock)
#ifdef _DEBUG_MTX
        , m_Name(Name)
#endif
    {
        m_x = 0;
        m_bUnLockerMode = false;
    }
    CMaaWin32Locker(T &Lock, const char * Name) noexcept
    :   m_Lock(Lock)
        //  , m_Name(Name)
    {
        m_x = 0;
        m_bUnLockerMode = false;
    }
    CMaaWin32Locker(T &Lock) noexcept
    :   m_Lock(Lock)
    {
        m_x = 0;
        m_bUnLockerMode = false;
    }
    ~CMaaWin32Locker() noexcept
    {
        if  ((m_x > 0 && !m_bUnLockerMode) || m_x != 0)
        {
            while(m_x > 0)
            {
                m_Lock.UnLockM();
                --m_x;
            }
            while(m_x < 0)
            {
                m_Lock.LockM();
                ++m_x;
            }
        }
    }
#ifndef DEBUG_MTX
    _dword Lock() noexcept
    {
        _dword x =
#ifdef DEBUG_MTX
        m_Lock.LockF("unknown", -2);
#else
        m_Lock.Lock();
#endif
        if  (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
        {
            ++m_x;
        }
        return x;
    }
#endif
    _dword Lock(const char * txt) noexcept
    {
        _dword x =
#ifdef DEBUG_MTX
        m_Lock.LockF("unknown", -2);
#else
        m_Lock.Lock();
#endif
        if  (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
        {
            ++m_x;
        }
        return x;
    }
    _dword LockF(const char * file, int line) noexcept
    {
        _dword x = m_Lock.LockF(file, line);
        if  (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
        {
            ++m_x;
        }
        return x;
    }
    //     DWORD Lock(DWORD dwTimeToWait);
    int UnLockF(const char * file, int line) noexcept
    {
        if  (m_x <= 0 && !m_bUnLockerMode)
        {
            return -1;
        }
        --m_x;
        return m_Lock.UnLockF(file, line);
    }
    int UnLock() noexcept
    {
#ifdef DEBUG_MTX
        return UnLockF("unknown", -2);
#else
        if  (m_x <= 0 && !m_bUnLockerMode)
        {
            return -1;
        }
        --m_x;
        return m_Lock.UnLock();
#endif
    }
    void SetUnLockerMode(bool bSet = true) noexcept
    {
        m_bUnLockerMode = bSet;
    }
#ifndef _DEBUG_MTX
#undef m_Name
#endif
};
/*
_dword CMaaWin32Locker::Lock(_dword dwTimeToWait)
{
     DWORD x = m_Lock.Lock(dwTimeToWait);
     if   (x == WAIT_OBJECT_0 || x == WAIT_ABANDONED)
     {
          ++m_x;
     }
     return x;
}
*/
#endif

#ifndef TOOLSLIB_SINGLE_THREAD

//#define SAFE_GLOCKS
#ifdef SAFE_GLOCKS
// exception safe
#define CMaaWin32Locker_aLocker_gLock_Atomic CMaaWin32Locker<CMaaLiteMutex> aLocker(gLock_Atomic)
#define aLocker_Lock aLocker.Lock()
#define aLocker_UnLock aLocker.UnLock()

#define CMaaWin32Locker_a2Locker_gLock_Atomic2 CMaaWin32Locker<CMaaLiteMutex> a2Locker(gLock_Atomic2)
#define a2Locker_Lock a2Locker.Lock()
#define a2Locker_UnLock a2Locker.UnLock()

#define CMaaWin32Locker_Locker_gLock CMaaWin32Locker<CMaa_gLock_lib_Mutex> Locker(gLock_lib)
#define Locker_Lock Locker.Lock()
#define Locker_UnLock Locker.UnLock()
#else
// exception unsafe
#define CMaaWin32Locker_aLocker_gLock_Atomic //CMaaWin32Locker<CMaaLiteMutex> aLocker(gLock_Atomic)
#define aLocker_Lock gLock_Atomic.Lock()
#define aLocker_UnLock gLock_Atomic.UnLock()

#define CMaaWin32Locker_a2Locker_gLock_Atomic2 //CMaaWin32Locker<CMaaLiteMutex> a2Locker(gLock_Atomic2)
#define a2Locker_Lock gLock_Atomic2.Lock()
#define a2Locker_UnLock gLock_Atomic2.UnLock()

#define CMaaWin32Locker_Locker_gLock //CMaaWin32Locker<CMaa_gLock_lib_Mutex> Locker(gLock_lib)
#define Locker_Lock gLock_lib.Lock()
#define Locker_UnLock gLock_lib.UnLock()
#endif

// exception safe
//#define CMaaWin32Locker_aLocker_gLock_Atomic_ CMaaWin32Locker<CMaaLiteMutex> aLocker(gLock_Atomic)
//#define aLocker_Lock_ aLocker.Lock()
//#define aLocker_UnLock_ aLocker.UnLock()

#define CMaaWin32Locker_a2Locker_gLock_Atomic2_ CMaaWin32Locker<CMaaLiteMutex> a2Locker(gLock_Atomic2)
#define a2Locker_Lock_ a2Locker.Lock()
#define a2Locker_UnLock_ a2Locker.UnLock()

//#define CMaaWin32Locker_Locker_gLock_ CMaaWin32Locker<CMaa_gLock_lib_Mutex> Locker(gLock)
//#define Locker_Lock_ Locker.Lock()
//#define Locker_UnLock_ Locker.UnLock()

#else

#define CMaaWin32Locker_aLocker_gLock_Atomic
#define aLocker_Lock
#define aLocker_UnLock

#define CMaaWin32Locker_a2Locker_gLock_Atomic2
#define a2Locker_Lock
#define a2Locker_UnLock

#define CMaaWin32Locker_Locker_gLock
#define Locker_Lock
#define Locker_UnLock

#define CMaaWin32Locker_a2Locker_gLock_Atomic2_
#define a2Locker_Lock_
#define a2Locker_UnLock_

#endif

#define CMaaWin32Locker_ CMaaWin32Locker<CMaa_gLock_usr_Mutex>
